#ifndef HTTPER_H
#define HTTPER_H

#include <QObject>
#include <functional>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonDocument>

#include <QNetworkRequest>
#include <QNetworkReply>
#include <QNetworkAccessManager>
#include <QTimer>
#include <QByteArray>

#include <QFile>
#include <QSharedPointer>

/**
 * @brief The Httper class
 * FIXME:
 * 1 应该每一个get或者post有对应的name，然后有一个表去维护所有name-reply，不应该现在这样限制只有一个reply;
 * 2 回调block时需要注意是queue还是direct，建议是queue；
 */

class Httper : public QObject
{
    Q_OBJECT
public:
    explicit Httper(const QString &host, QObject *parent = nullptr);
    ~Httper();

    bool isRunning();
    void cancel();

    bool post(const QString &api, const QMap<QString,QString> &param, const QMap<QString,QString> &header={},const QVariant &body=QVariant(), std::function<void(QString,QVariant)> resultBlock = nullptr, int timeout = -1);
    bool post(const QString &host, const QString &api, const QMap<QString,QString> &param, const QMap<QString,QString> &header={},const QVariant &body=QVariant(), std::function<void(QString,QVariant)> resultBlock = nullptr, int timeout = -1);
    bool post(const QUrl &url, const QMap<QString,QString> &header={},const QVariant &body=QVariant(), std::function<void(QString,QVariant)> resultBlock = nullptr, int timeout = -1);

    bool get(const QString &api, const QMap<QString,QString> &param, const QMap<QString,QString> &header={},std::function<void(QString,QVariant)> resultBlock = nullptr, int timeout = -1);
    bool get(const QString &host, const QString &api, const QMap<QString,QString> &param, const QMap<QString,QString> &header={},std::function<void(QString,QVariant)> resultBlock = nullptr, int timeout = -1);
    bool get(const QUrl &url, const QMap<QString,QString> &header={},std::function<void(QString,QVariant)> resultBlock = nullptr, int timeout = -1);

    /**
     * @brief uploadFile - deprecated, use uploadFile(const QUrl &url, const QString &filePath, const QString &fileKey, const QVariantMap &formData, std::function<void(QString,QVariant)> resultBlock, int timeout = -1)
     * @param url - target url
     * @param filePath - multipart/form-data param, key is file, the path of file will be upload, ex: file:%filePath%
     * @param formData - multipart/form-data param, {key:{type:'',content:''}}
     * @param resultBlock - result
     * @param timeout - timer waiting duration (ms), -1 is no timer
     * @return
     */
    bool uploadFile(const QUrl &url, const QString &filePath, const QVariantMap &formData, std::function<void(QString,QVariant)> resultBlock, int timeout = -1);
    /**
     * @brief uploadFile - FIXME: Should not finish directly through call return, must be through block or signal.
     * @param url - target url
     * @param filePath - multipart/form-data param, the content of file item in the multipart/form-data
     * @param fileKey - multipart/form-data param, the key of file item in the multipart/form-data, ex: %fileKey%:%filePath%
     * @param formData - multipart/form-data param, {key:{type:'',content:''}}
     * @param resultBlock - result
     * @param timeout - timer waiting duration (ms), -1 is no timer
     * @return
     */
    void uploadFile(const QUrl &url, const QString &filePath, const QString &fileKey, const QVariantMap &formData, std::function<void(QString,QVariant)> resultBlock, int timeout = -1, QString name = "");

    void setEventBlock(std::function<void(QString,QVariant)> eventBlock){_eventBlock = eventBlock;}

private:
    Q_SLOT void timer_onTimeout();
    Q_SLOT void reply_onFinished();
    Q_SLOT void reply_onReadReadyForEvent();
    Q_SLOT void reply_onReadReadyForProgress();
    Q_SLOT void reply_onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
    Q_SIGNAL void sigResult(QString errorString, QVariant response);
    Q_SLOT void onResult(QString errorString, QVariant response);

    void startTimer(int ms);
    void stopTimer();
    QString suffixNameToApplicationType(const QString &suffixName);

private:
    bool _isCanceled;
    QString _host;
    QNetworkAccessManager _manager;
    QNetworkRequest _request;
    QNetworkReply *_reply;
    std::function<void(QString,QVariant)> _resultBlock;
    std::function<void(QString,QVariant)> _eventBlock;
    std::function<void(qint64,qint64)> _progressBlock;
    QTimer *_timer;
    QByteArray _bufferForRead;
    QString _name;

    enum RequestType{
        GET,
        POST,
        UPLOADFILE,
    };
    RequestType _type;
};

#endif // HTTPER_H
